package de.hs.rm.UJAdapter.factory;

import java.io.IOException;
import java.util.ArrayList;

import javax.management.AttributeChangeNotification;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanNotificationInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerDelegateMBean;
import javax.management.ObjectName;
import javax.management.openmbean.OpenMBeanAttributeInfoSupport;
import javax.management.openmbean.OpenMBeanParameterInfoSupport;
import javax.management.openmbean.OpenType;

import org.osgi.service.upnp.UPnPDevice;
import org.osgi.service.upnp.UPnPService;

import de.hs.rm.UJAdapter.event.impl.jmx.UJAttributeChangeNotificationListener;
import de.hs.rm.UJAdapter.impl.UJAction;
import de.hs.rm.UJAdapter.impl.UJDevice;
import de.hs.rm.UJAdapter.impl.UJService;
import de.hs.rm.UJAdapter.impl.UJStateVariable;
import de.hs.rm.UJAdapter.util.ClassTypeUtil;
import de.hs.rm.UJAdapter.util.StringGenerationUtil;

public abstract class UJServiceFactory {
	
	public static UPnPService createService(
			UJDevice upnpDevice,
			MBeanServerDelegateMBean delegateProxyMBean,
			MBeanInfo mbeaninfo,
			MBeanServerConnection mBeanServerConnection,
			ObjectName objName)
	{
		//set general service information
		UJService mBeanService = new UJService();
	    mBeanService.setId(StringGenerationUtil.getServiceIDfromObjName(objName));
	    mBeanService.setType(StringGenerationUtil.getServiceTypefromObjName(objName));
	    MBeanAttributeInfo[] mbeanAttributes = mbeaninfo.getAttributes();
	    mBeanService.setDevice(upnpDevice);
	    //generate device state variables from mbean attributes	    
	    ArrayList<UJStateVariable> stateVariablesList = new ArrayList<UJStateVariable>();
	    ArrayList<UJStateVariable> genericOpenStateVariablesList
			= new ArrayList<UJStateVariable>();
	    ArrayList<UJStateVariable> openTypeStateVariablesList
			= new ArrayList<UJStateVariable>();
	    ArrayList<OpenMBeanAttributeInfoSupport> openMBeanAttributeList
	    	= new ArrayList<OpenMBeanAttributeInfoSupport>();
	 	//generate normal state variables
	    for(int i = 0; i < mbeanAttributes.length; i++)
	    {
	    	//from complex type (aka open type) attributes
	    	if(mbeanAttributes[i].getClass().toString().contains("OpenMBeanAttributeInfoSupport"))
	    	{
	    		OpenMBeanAttributeInfoSupport openMBeanAttr = (OpenMBeanAttributeInfoSupport) mbeanAttributes[i];
	    		OpenType openType = openMBeanAttr.getOpenType();
	    		openMBeanAttributeList.add(openMBeanAttr);
	    		UJStateVariableFactory.createOpenTypeStateVariableList(openType, genericOpenStateVariablesList);
	    		openTypeStateVariablesList.add(UJStateVariableFactory.createOpenTypeStateVariable(openMBeanAttr));
	    	}
	    	// from primitive type attributes
	    	else
	    		stateVariablesList.add(UJStateVariableFactory.createStateVariable(mbeanAttributes[i]));
	    }	    
	    
	    //generate actions
	    ArrayList<UJAction> actionList = new ArrayList<UJAction>();
	    ArrayList<UJAction> openTypeActionList = new ArrayList<UJAction>();
	    
	    //generate device state variable setters/getters from mbean attributes
	    for (UJStateVariable sV : stateVariablesList)
	    {
	    	if(sV.isWritable())
	    	{
	    		actionList.add(UJActionFactory.createSetter(mBeanServerConnection, sV, objName));
	    	}
	    	
	    	if(sV.isReadable())
	    	{
	    		actionList.add(UJActionFactory.createGetter(mBeanServerConnection, sV, objName));	    		
	    	}
	    }
	    
	    //generate device state variable setters/getters from mbean open type attributes
	    for (OpenMBeanAttributeInfoSupport oTA : openMBeanAttributeList)
	    {
	    	if(oTA.isWritable())
	    	{
	    		openTypeActionList.add(UJActionFactory.createOpenTypeSetter(mBeanServerConnection, oTA, genericOpenStateVariablesList, objName));
	    	}
	    	
	    	if(oTA.isReadable())
	    	{
	    		openTypeActionList.add(UJActionFactory.createOpenTypeGetter(mBeanServerConnection, oTA, genericOpenStateVariablesList, objName));	    		
	    	}
	    }
	    actionList.addAll(openTypeActionList);
	   
	    //generate actions out of operations
	    MBeanOperationInfo[] mbeanOperations = mbeaninfo.getOperations();
	    ArrayList<UJStateVariable> genericStateVariables
			= new ArrayList<UJStateVariable>();
	    genericStateVariables.addAll(genericOpenStateVariablesList);
	    
	    ArrayList<String> operationArguments
	    	= new ArrayList<String>();
	    
	    String upnpName = "";
	    
	    for(int i = 0; i < mbeanOperations.length; i++)
	    {
	    	upnpName = mbeanOperations[i].getName();
	    	if(actionExits(actionList, upnpName))
	    	{
	    		upnpName = upnpName + "_" + i;
	    	}
	    	operationArguments.add(mbeanOperations[i].getReturnType());
	    	MBeanParameterInfo[] mbeanParams = mbeanOperations[i].getSignature();
	    	for(int j = 0; j < mbeanParams.length; j++)
	    	{
	    		operationArguments.add(mbeanParams[j].getType());
	    		if(mbeanParams[j].getClass().toString().contains("OpenMBeanParameterInfoSupport"))
		    	{
	    			OpenMBeanParameterInfoSupport openmbeanParam = (OpenMBeanParameterInfoSupport)mbeanParams[j];
	    			OpenType openType = openmbeanParam.getOpenType();
	    			UJStateVariableFactory.createOpenTypeStateVariableList(openType, genericOpenStateVariablesList);
		    	}
	    	}
    	
    	
	    	if(ClassTypeUtil.checkListForOpenType(operationArguments))
	    		actionList.add(UJActionFactory.createOperationOpenType(mBeanServerConnection, mbeanOperations[i], genericStateVariables, objName, upnpName));
	    	else
	    	   	actionList.add(UJActionFactory.createOperation(mBeanServerConnection, mbeanOperations[i], genericStateVariables, objName, upnpName));
	   }
	    
	    //add generic state variables to sv list
	    stateVariablesList.addAll(genericStateVariables);
	    stateVariablesList.addAll(openTypeStateVariablesList);
	    
	    //convert state variable list to sv array
	    UJStateVariable[] stateVariables =
	    		new UJStateVariable[stateVariablesList.size()];
	    for (int i = 0; i < stateVariablesList.size(); i++)
	    {
	    	stateVariables[i] = stateVariablesList.get(i);
	    }
	    
	    //set service stat variables
	    mBeanService.setStateVariables(stateVariables);
	    
	    //convert actionlist to action array
	    UJAction[] actions =
	    		new UJAction[actionList.size()];// + mbeanOperations.length];
	    	    
	    for (int i = 0; i < actionList.size(); i++)
	    {
	    	actions[i] = actionList.get(i);
	    }
	    
	    //set service actions 
	    mBeanService.setActions(actions);
	    
	    //Add Notifications for Attribute Changes
	    MBeanNotificationInfo[] mBeanNotifications = mbeaninfo.getNotifications();
		
		for(MBeanNotificationInfo mBeanNotif : mBeanNotifications)
		{
			for (String notifType:mBeanNotif.getNotifTypes())
			{
				if(notifType.equals(AttributeChangeNotification.ATTRIBUTE_CHANGE))
				{
					 UJAttributeChangeNotificationListener notificationListener = new UJAttributeChangeNotificationListener();
					 notificationListener.setListenerProps(upnpDevice, mBeanService);
			         try {
						mBeanServerConnection.addNotificationListener(objName, notificationListener, null, null);
						mBeanService.setNotificationListener(notificationListener);
					} catch (InstanceNotFoundException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (IOException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
					}
				}
			}
		}
	   
	    return mBeanService;
	}
	
//	public static UPnPService createNotificationService(
//			UPnPDevice upnpDevice,
//			MBeanServerDelegateMBean delegateProxyMBean,
//			MBeanServerConnection mBeanServerConnection,
//			ObjectName objName,
//			String notificationName)
//	{
//		//set general service information
//		UJService mBeanService = new UJService();
//	    mBeanService.setId("urn:JMX:serviceID:JMXNotifications");
//	    mBeanService.setType("urn:JMX:Notifications");
//	    mBeanService.setDevice(upnpDevice);
//	    UJStateVariable[] stateVariables = {UJStateVariableFactory.createNotificationStateVariable(notificationName)};
//	    mBeanService.setStateVariables(stateVariables);
//	    return mBeanService;
//	
//	}
	
	private static boolean actionExits(ArrayList<UJAction> aList, String operationName)
	{
		for (int i = 0; i < aList.size(); i ++)
			if(aList.get(i).getName() == operationName)
				return true;
		return false;		
	}
}
